package service

import (
	"context"
	"fmt"
	"github.com/kevinlisr/install-levault/vari"
	"github.com/minio/minio-go/v7"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type MinioExecutor struct {
	minioClient *minio.Client
}

func NewMinioExecutor(miniCli *minio.Client) *MinioExecutor {
	return &MinioExecutor{
		minioClient: miniCli,
	}
}

func (m *MinioExecutor) CreateBucket(bucketName, location string) error {
	if bucketName == "" {
		bucketName = "nancal-test"
	}

	if location == "" {
		location = "cn-north-1"
	}

	err := m.minioClient.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{
		Region: location,
	})
	if err != nil {
		var exists bool
		// 检查存储桶是否已经存在。
		exists, err = m.minioClient.BucketExists(context.Background(), bucketName)
		if err == nil && exists {
			vari.Logger.Info(fmt.Sprintf("桶 %s 已经存在!", bucketName))
		} else {
			vari.Logger.Error(fmt.Sprintf("桶 %s 创建失败 %s", bucketName, err.Error()))
			return err
		}
	}
	vari.Logger.Info(fmt.Sprintf("桶 %s 创建成功\n", bucketName))

	return nil
}

func (m *MinioExecutor) UploadFile(bucketName, dirPath string) error {
	err := m.uploadDirectory(bucketName, dirPath, "")
	if err != nil {
		vari.Logger.Fatal(err.Error())
	}
	vari.Logger.Info(fmt.Sprintf("File Upload to bucket %s completed.", bucketName))
	return nil
}

// uploadDirectory recursively uploads a directory to the specified bucket.
func (m *MinioExecutor) uploadDirectory(bucketName, dirPath, prefix string) error {
	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			vari.Logger.Error("filepath walk uploadDirectory failed, " + err.Error())
			return err
		}

		if !info.IsDir() {
			relativePath := strings.TrimPrefix(path, dirPath)
			objectName := filepath.Join(prefix, relativePath)
			objectName = strings.ReplaceAll(objectName, "\\", "/") // Replace backslashes with forward slashes for MinIO

			vari.Logger.Info(fmt.Sprintf("Uploading %s to %s\n", path, objectName))
			return uploadFile(m.minioClient, bucketName, objectName, path)
		}

		return nil
	})
	if err != nil {
		return fmt.Errorf("error walking the path %s: %v", dirPath, err)
	}
	return nil
}

// uploadFile uploads a single file to the specified bucket.
func uploadFile(client *minio.Client, bucketName, objectName, filePath string) error {
	ctx := context.Background()
	_, err := client.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{})
	if err != nil {
		return fmt.Errorf("failed to upload %s: %v", filePath, err)
	}
	return nil
}

func (m *MinioExecutor) DownLoadFile(ctx context.Context, bucketName, objectName string) ([]byte, error) {
	object, err := m.minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
	if err != nil {
		vari.Logger.Error(fmt.Sprintf("Download from minio error:{%s} object: {%s}\n", err.Error(), objectName))
		return nil, fmt.Errorf("Download from minio error:{%s} object: {%s}\n", err.Error(), objectName)
	}
	chunk, err := ioutil.ReadAll(object)
	if err != nil {
		if strings.Contains(err.Error(), "does not exist") {
			vari.Logger.Error(fmt.Sprintf("read to fd fail %s\n", err.Error()))
		} else {
			return nil, fmt.Errorf("read to fd fail %s\n", err.Error())
		}
	}
	return chunk, nil
}
